Well Monday was a busy day - well, in terms of coding, if nothing else. I started looking on Sunday at a new module for phpmole, - a simple debugger that could just look at the output, find the warnings, turn them into links and allow you to jump to them.
All sounded Quite easy, however from previous experience, It's not quite that easy.
Details Inside.....
The key issue is that when you initiate a process
(popen) in php, it will hang and wait upon responses - effectivly a
blocking IO process, this is standard with the Unix definition of
popen, which exec,system and popen all use. So, after working all that
out, it was obvious that there no simple fix that could be applied to
popen to turn it into non-blocking.
So I started looking at alternatives - the first being pcntl -a
new(ish) extension in the php cvs, which deals with process controll.
This module makes available commands like pcntl_fork(), and
pcntl_signal(SIG???,"callback"), the key understanding fork is
that it does not start the program from the beginning, but rather at
the point in time where the fork command is, it breaks the process in
two, both have all the variables available, and are at the same point,
however from then on, they operate as seperate entities.
the return value for fork is either (0) - which means that I'm the
child, or a number=PID, which means I'm the parent, and the PID is of
the child that has started...
This meant that the design of the application went something like
this at the point of exec'ing the child process to monitor, the program
forks. the child process is responsible for starting the exec - eg.
running the program and waiting for data to come in, so this is the one
that hangs. The child process would then have to output the data
to a file as it came in, and issue a signal to the parent to let it
know to look at the file.
It was a bit messy, but in theory should have worked. However after
installing pcntl as a module (not compiled into the php binary). It
became clear that
1.pcntl_signal did handle class/method callbacks
2.the callbacks where not getting called.
The class/method callback stuff took a while, as my understanding
of the Zend API, is flakey at the best of times, but eventually I got
it working
Overview of the pcntl module
The internals of the pcntl module do not directly 'call a user
function' as soon as it recieves a signal, this could be a 'bad' thing,
(as it may be in the middle of a command). so it basically keeps a
queue of signals in a standard php internal hash, and when the
zend_extension_statement_handler is called (by the Zend interpreter),
it loops through this queue and calls all the functions associated with
the signals.
This had a few problems (as mentioned above), first was that the
associated hash table that stored the mappings of signal to functions
only used a string as it's association, rather than a zval (the basic
storage type for php variables in C). The changes here involved
modifing the input checking area, - so that it accepted array(&$this,"method") and
"function", and then stored it in the hash table correctly - this took
alot of working out, as it had to replicate the data before it could
store it in the array (otherwise it got freed from memory at the end of
the function call).
At the other end, the hash table had to get it out in the right
way, and use it in the call_user_function call correctly. (basically
getting the right combination of zval ** handle, zval * handle ,
zend_is_callable then- either *handle, &handle or just plain
handle.. - This is why php is so nice, not having to wory about
pointers :)
The other issue was that as I was building module as a
dl'extension, none of the zend_init or statement_handler where ever
getting called, the solution to this was quite simple. - just add
a php function to clear/check the queue. - this could then either be
used randomly all over the program, or in php-gtk's case, in a loop
with while(gtk::events_pending()) gtk::main_iteration();
Looking at the process stuff
by the time I finished looking at all that, It was getting late,
and I started playing with sockets, the idea being, that the way I
described above seemed a little clunky - the signal passing to tell one
fork to check a file.. , so I started looking at non-blocking unix
domain sockets - but thats another story........